#include "../util/util.h"

#include <fcntl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/wait.h>

// #define RANDOM_PATTERN
#define STACK_SIZE 16384

static volatile uint64_t *sending = 0;

static __attribute__((noinline)) int sender(void *varg)
{
	uint64_t first = 0xffffffff00000000;
	uint64_t second = 0x00000000ffffffff;
	uint64_t count = 8;
	int i = 0, repetitions = 10000; // if too large, the sender will send past an interval

	while (1) {

		if (*sending == 1) {

			for (i = 0; i < repetitions; i++) {

				// Sending 1s (high freq)
				asm volatile(
					"shlx %0, %1, %%rbx\n\t"
					"shlx %0, %1, %%rcx\n\t"
					"shlx %0, %1, %%rsi\n\t"
					"shlx %0, %1, %%rdi\n\t"
					"shlx %0, %1, %%r8\n\t"
					"shlx %0, %1, %%r9\n\t"
					"shlx %0, %1, %%r10\n\t"
					"shlx %0, %1, %%r11\n\t"
					"shlx %0, %1, %%r12\n\t"
					"shlx %0, %1, %%r13\n\t"
					"shlx %0, %1, %%r14\n\t"
					"shlx %0, %1, %%r15\n\t"

					:
					: "r"(count), "r"(first)
					: "rbx", "rcx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15");
			}

		} else {

			for (i = 0; i < repetitions; i++) {

				// Sending 0s (low freq)
				asm volatile(
					"or %0,%%r8\n\t"
					"or %0,%%r9\n\t"
					"or %0,%%r10\n\t"
					"or %0,%%r11\n\t"
					"or %1,%%r8\n\t"
					"or %1,%%r9\n\t"
					"or %1,%%r10\n\t"
					"or %1,%%r11\n\t"

					:
					: "r"(first), "r"(second)
					: "r8", "r9", "r10", "r11");
			}
		}
	}

	return 0;
}

int main(int argc, char *argv[])
{
	int i;

	// Check arguments
	if (argc != 3) {
		fprintf(stderr, "Wrong Input! Enter channel interval and ntasks!\n");
		printf("Enter: %s <interval>\n", argv[0]);
		exit(1);
	}

	// Parse channel interval
	uint64_t interval = 1; // C does not like this if not initialized
	sscanf(argv[1], "%" PRIu64, &interval);
	if (interval <= 0) {
		printf("Wrong interval! interval should be greater than 0!\n");
		exit(1);
	}

	// Parse ntasks
	int ntasks;
	sscanf(argv[2], "%d", &ntasks);
	if (ntasks < 0) {
		fprintf(stderr, "ntasks cannot be negative!\n");
		exit(1);
	}

#ifdef RANDOM_PATTERN
	// 8 kB of random bits
	char *pattern = "01011000111100110110000000010000011100001110111101101011010011101011100111001100011000111101001101010101111110100110110110001110001000111100010001101000001111011000111000100100011100000000110011001111001010000100001001110100000111011110111000011110011111000000100100011001110110000110001001011100000110010011111010000001101011010101100011011000110110100010101001011010111010111010010000110001000101100100011010101111100001101100000111010001001000100110101100010100111000001001000000011111111101101100011011101001010001101011100111001110011011110000010110011000101001001001111101110100000011010011110100010000011100110111010100011000101111100010100001101111011000001100001100101111011111110110000111111011110100100000100100010111000001101000010000110100011100010000110000000001011101000100010111001001111001111001001100101001111000111100010100011111110110110000100100111110011110111001011110111110110011110101001001000011100010101010011001110010111110000001001010110000010000010101001110001111011101000010000011101101110001000111011000000101111100100100001110011000101100011100000101001000000001010110001011110000000011011000100111011010000111010111101100001100000010110100010001100000111100011101010011000101100110110110110111010000110111010001011001111101001110000010100010110000110001101111101111001000100010010101100100101110100000101110101100001001101011010100011100111001100010110000101100111000000101100001010000001000111001000001110110101110111000001110000000011011011100010111001010010101100100101011001010011110111101000100100000010011001100010110001001010011011100010000010100001100101110011101011000011100101100000111111101011100111110101011101011111011010001101110110110110100001101100101101110010011011100010010000110001101110100011000001011111010000101010101010011011100001000101111011110100011100001010101010100110110001110010110010011101000001100111100110101010001110001010101001110100101111011100101010100101011000100100111010111000110001100010100010100100100110111101010100011000100110000111100111011000000001010011011010010000111101110000010101100111001010000011101011001100111010111110101101110110010011010100001010100011110011000000101111111010010100000000000110101010110111011011110101101000010010110001000000001101101111011000001011011011001101001010110011100011110011000000101010101101000111001000001011011011110001110001101101010010000011011110101000100000101001101100110010110110100101100111110000100111110011110011101100001001110011100010110001000110110011001011101001000101110010111110010111101000101011111111111010110010001011100111101101111001110101101000011101011000110110110000111001111101101111011100001111010110101010100110010011111000000000011100010101001010011111111011001001011110001111000110001111010110000100011010000000001010110111110111011111111011011011011100010000011101001101011110101100100101001011111110001011111000001111110000001001101001100100001001001011000100100111100010010001111111111001001110000110111110000000000111010001110010111100011101100110010100100111111101000100000011000000010111110000111001000111100111010111101101101000001000011010111000100001110110000010100000000111101111001100101001011001000100100111001010100100111010000101101011000100011100110000101101110101100000110010110110010001101010110111001101101001101001000000000110010000000101110111000101010001010010001111101101101001110101110011000011111000110011110000101110110101100001110001000111110000100100011101001100111011000101010001101100000011011100010001000000000110000100010011000001000001100010100011001011100110011111000000110111100000001000101100001000010101011010011000010111101100101011001110101110101111000101100110010001111010111000000101001010010101011010011110101111100010100000111101100101001010111011000111100010000001100100111100101100111111010110011111100101100101100101110010001101110100001110101000110001001100001110001001100110010010111011101110011000001111011010010101011101110000010001000010100110000001101010011000100111110100100101010000100001011011100110011011101100110110101011111101100000010000010011101110010110101111011000001010101111001000100110111011100101101000100111001100111101011101010011000011000001110100100101011000101010011000010101010000111100011000010000101110011000001101100001110001001010101010011000100000000101011111000011000000001010001111001001010010010110000010010010001111100101101010011000011100000011111000010111100111100101001101100010110110000010010001001101000010100111001000010000100011101011010000101011011111001011100010001010010111111000011000101000010001000100101000001100101100001010001111111010100010000100001011100100100101100100000110000101000010101010000011100001001011010101101111101001110100010010011101001000110000101100011100001001101100010100101111111101110100110011011101010000101101010110000011000010100101001111011110101110010001100111001111001000001000001000001000010101100011010111111011100000111011110000011010101011010111100001111001011010011110000010100011101110100000001011010001000100001010110100100000111000011000111011101000011111000101000101111101100001101110011010001111111001011111000000000110010111000011000111011110010111111110111100000110010110101011100011010111100011111100000101101110010010110100110111100101000111001101011111100101011111101000000100110111001110110110011101001001000001100001001000100110000101000001000110111110101000100111100111000011011011000110011010101010000100000100001011100111010101010100110001001000101111001000111100010100011011110101010000111001110100000011001010100001011111110110100111100100000100011000111011101011001010100001101010111111110100001010011001000000101111011101110000001010001101111011011000110101000000011101010111101111011101100100001110001001000100100011011010110010010100001100000010100110100001011001001001001100000011101110101000011101111110111101010100011010001011000110100111110110110110001010111110110000000111010010010010110101010010100111110111100011110101100110011111001001000000110101010001010010111011111000010000010100111000101000001011110010100001100101100101001100000011000110101000000010011011011101111110001111110110111000110110110001001101011100010011001000011010010010010010111101100101011001101110000001100001111000100001001000110100001010110011100111001101101001111101111101010110011100101111000001100101000111001101111100011001000010000101111010111100110101100011001111110001001111000100101001001011001001010000100011000110011111101000000110110001111100111111100100001100010010111001010001100000001000010100110011100101111101010011000101101110011010110100010100111100000010011100111001000001010100011101000001000011110011010001110010001111000101000011010101100110000111100101110000110001100111010011110011100101001000111111010100000100001010001100100011100000101010101001101000101111001111111111001100100011001010100010000111111010110010100011001110100111001010110000100010111001100101101011111111000101000111110001111100110001011101001010100111101111000001000001000000100010011011010110110000010100000111111100011000111100111101111011100011001011111001100001111101001010010111001010000000101010100001001101101110110111100111110011000010110010111001110111111101001000001100110100110111110010010111110101000111100010111010111011001111110100001000001100101100000100001011101111011000010100001100101100110010111111000101101001100001000101001101011101011111011100101111110111111111111101011110111011010100101110000100011110010111000000001001110100110110110000001000100101110011010011100111011111111110000110111010111011010000110000111010100110010000101000100110011100111001101001101100101011000111101101010110101000000101000111111011101101111010000111000101101100110010111001001001000000101110011001100110001010001100000000010000011110100010010010101111000011110011010000010010110011110101001000101101000110000100100101111111001100111110011100100110010011001011011110011011101010110111110011111001011001011010001011011111010000110000100100011001110111011010111110110101001110010100010100100110111001001100100100111010010110100";
	int patternlen = strlen(pattern);
#endif

	// This variable is shared between threads
	sending = malloc(sizeof(*sending));
	*sending = 0;

	// Allocate memory for the threads
	char *tstacks = mmap(NULL, (ntasks)*STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

	// Start victim threads
	int tids[ntasks];
	for (int tnum = 0; tnum < ntasks; tnum++) {
		tids[tnum] = clone(&sender, tstacks + (ntasks - tnum) * STACK_SIZE, CLONE_VM | SIGCHLD, NULL);
	}

	// Prepare to send
	uint64_t start_t;
	uint64_t time;

	// Synchronize
	do {
		start_t = get_time();
	} while ((start_t % interval) > 10);

	// Send bits
	for (time = 0; time < UINT64_MAX; time++) {

#ifdef RANDOM_PATTERN
		*sending = pattern[time % patternlen] - '0';
#else
		*sending = time % 2;
#endif

		// Let the sending continue for the interval duration
		while ((get_time() - start_t) < (interval * time)) {}
	}

	// Kill victim threads
	for (int tnum = 0; tnum < ntasks; tnum++) {
		syscall(SYS_tgkill, tids[tnum], tids[tnum], SIGTERM);

		// Need to join o/w the threads remain as zombies
		// https://askubuntu.com/a/427222/1552488
		wait(NULL);
	}

	// Clean up
	munmap(tstacks, (ntasks + 1) * STACK_SIZE);

	return 0;
}
